データキャッシュ
============

データキャッシュは、PHP 変数をキャッシュし、後でそのキャッシュから読み込めるようにします。
この目的のために、キャッシュコンポーネントのベースクラス [CCache] は、多くの場合に利用される [set()|CCache::set] と [get()|CCache::get] の二つのメソッドを提供します。

キャッシュに変数 `$value` を保存するには、ユニークな ID を選んで [set()|CCache::set] を呼びます:

~~~
[php]
Yii::app()->cache->set($id, $value);
~~~

キャッシュされたデータは、何らかのキャッシングポリシー (たとえば、キャッシュ容量がいっぱいになれば、一番古いデータが削除される、など) によって削除されない限りは、ずっと残ります。
この挙動を変えるために、[set()|CCache::set] を呼ぶときに、有効期限パラメータを指定して、長くとも一定の期間の後には、必ずキャッシュが削除されるようにする事もできます。

~~~
[php]
// 最大 30 秒間、キャッシュに値が保持される
Yii::app()->cache->set($id, $value, 30);
~~~

後で (同じウェブリクエスト、あるいは、別のウェブリクエストの中で) この変数にアクセスする必要が生じたときに、その ID を指定して [get()|CCache::get] を呼ぶと、キャッシュから変数を読み込むことが出来ます。
返り値が false である場合は、変数の値がキャッシュの中に無いことを意味しますので、値を再生成する必要があります。

~~~
[php]
$value=Yii::app()->cache->get($id);
if($value===false)
{
	// $value がキャッシュの中に見つからなかったため、
	// 後で利用できるように、再生成してキャッシュに保存します:
	// Yii::app()->cache->set($id,$value);
}
~~~

キャッシュされる変数のために ID を選ぶときは、アプリケーション中でキャッシュされる可能性のある他の全ての変数の中で、ユニークな ID が割り当てられるように保証してください。
ID は、アプリケーションをまたいでユニークになるように指定する必要はありません。
キャッシュコンポーネントは、異なるアプリケーションに対しては、自動的に異なる ID を割り当てます。

MemCache や APC のようなある種のキャッシュストレージにおいては、バッチモードにより複数のキャッシュデータを獲得する手段がサポートされており、これを利用すると、キャッシュ獲得のオーバヘッドを低減することが出来ます。
この機能を利用するために [mget()|CCache::mget] という名のメソッドが提供されています。
裏打ちするキャッシュストレージがサポートしていない場合でも、[mget()|CCache::mget] はこの機能をシミュレートして動作します。

キャッシュから、キャッシュされた値を削除するには、[delete()|CCache::delete] を呼びます。
また、全てのキャッシュを削除するには、[flush()|CCache::flush] を呼びます。
[flush()|CCache::flush] は、他のアプリケーションのデータも含めて、全てのキャッシュデータを削除しますので、このメソッドを呼ぶ際は、細心の注意を払ってください。

> Tip|ヒント: [CCache] は `ArrayAccess` により実装されているため、
> キャッシュコンポーネントは配列のように扱えます。下記に例を示します:
> ~~~
> [php]
> $cache=Yii::app()->cache;
> $cache['var1']=$value1;  // $cache->set('var1',$value1); と同等
> $value2=$cache['var2'];  // $value2=$cache->get('var2'); と同等
> ~~~

キャッシュ依存関係
----------------

キャッシュデータは、有効期限の設定に加えて、依存関係のあるデータの変更によって無効になる可能性があります。
たとえば、あるファイルの内容をキャッシュに保存している場合、ファイルが変更されたときには、キャッシュされたコピーを無効にし、キャッシュではなくファイルから最新のデータを読み込む必要があります。

この依存関係は [CCacheDependency] またはその子クラスのインスタンスとして表わされます。
[set()|CCache::set] を呼ぶ際に、キャッシュされるデータと共に、依存関係のインスタンスを指定します。

~~~
[php]
// 値は 30 秒間有効です
// さらに、依存関係にあるファイルが変更された場合、有効期限内でも無効になります
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('FileName'));
~~~

このようにすると、[get()|CCache::get] を呼び出してキャッシュから `$value` を取り出そうとしたときに依存関係が調査されます。
そして、依存関係が変更されていれば、データを再生成する必要があることを知らせるために、[get()|CCache::get] は false の値を返します。

以下は、利用可能なキャッシュ依存関係の概要です:

   - [CFileCacheDependency]: ファイルの最終更新日時が変更された場合に、依存関係が変更されます。

   - [CDirectoryCacheDependency]: ディレクトリや、サブディレクトリ下のファイルのいずれかが変更された場合に、依存関係が変更されます。

   - [CDbCacheDependency]: 指定された SQL 文のクエリ結果が変わった場合に、依存関係が変更されます。

   - [CGlobalStateCacheDependency]: 指定されたグローバルステートが変更された場合に、依存関係が変更されます。
グローバルステートは、アプリケーション内で、複数のリクエストおよび複数のセッションを横断して持続する変数です。
グローバルステートは、[CApplication::setGlobalState()] によって定義されます。

   - [CChainedCacheDependency]: チェーン上の依存関係のいずれかが変更された場合に、依存関係が変更されます。

   - [CExpressionDependency]: 指定された PHP 式の結果が変更された場合に、依存関係が変更されます。


クエリキャッシング
------------------

バージョン 1.1.7 以降、Yii はクエリキャッシングのサポートを追加しました。
クエリキャッシングは、データキャッシュの上に構築されており、DB クエリの結果をキャッシュに格納します。
そのため、将来同じクエリが要求された場合に、結果を直接キャッシュから得られるため、DB クエリの実行時間を短縮することが出来ます。

> Info|情報: ある種の DBMS (例えば [MySQL](http://dev.mysql.com/doc/refman/5.1/en/query-cache.html))
> は、DB サーバサイドでのクエリキャッシュもサポートしています。
> このサーバサイドでのクエリキャッシングと比較すると、Yii がここでサポートする同じ機能は、
> より一層柔軟であり、潜在的にはより一層効率的でしょう。 

### クエリキャッシングを有効にする

クエリキャッシングを有効にするには [CDbConnection::queryCacheID] が正当なキャッシュアプリケーションコンポーネント (デフォルトでは`cache`) を参照するようにしてください。

### DAO と共にクエリキャッシングを使用する

クエリキャッシングを使うには、DB クエリを行う際に [CDbConnection::cache()] メソッドをコールします。
例を示します。

~~~
[php]
$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
~~~

上記の文を実行する際に、Yii は、実行される SQL 文の正当な結果がキャッシュの中に有るかどうかを最初にチェックします。
このチェックは、以下の3つの条件をチェックすることによって行なわれます。

- キャッシュが SQL 文によってインデックスされるエントリをもっているかどうか。
- キャッシュエントリが有効期限切れになっていないかどうか (最初にキャッシュに格納されてから1000秒未満)。
- 依存性が変更されていないか (`update_time` の最大値がクエリ結果がキャッシュに格納された時と同じ値かどうか)

もし上記すべての条件が満たされていれば、キャッシュされた結果が直接キャッシュから返されます。
そうでなければ、SQL 文が DB サーバへ送信されて実行され、対応する結果がキャッシュに格納されて返されます。


### アクティブレコードと共にクエリキャッシングを使う

クエリキャッシングは [アクティブレコード](/doc/guide/database.ar) と共に使用することも可能です。
そうするには、同様の [CActiveRecord::cache()] メソッドを以下のように呼び出します。

~~~
[php]
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');
$posts = Post::model()->cache(1000, $dependency)->findAll();
// リレーショナル AR クエリ
$posts = Post::model()->cache(1000, $dependency)->with('author')->findAll();
~~~

ここでの `cache()` メソッドは本質的には [CDbConnection::cache()] へのショートカットです。
内部的にはアクティブレコードによって生成された SQL 文を実行する際に、Yii が前節で示したようなクエリキャッシングを行います。


### 複数クエリのキャッシング

デフォルトでは、[CDbConnection] または [CActiveRecord] のどちらであっても、`cache()` メソッドを呼び出すたびに、次に実行する SQL クエリがキャッシュされる対象としてマークされます。
その他の SQL クエリは、`cache()` を再び呼ばない限り、キャッシュされません。
例えば、

~~~
[php]
$sql = 'SELECT * FROM tbl_post LIMIT 20';
$dependency = new CDbCacheDependency('SELECT MAX(update_time) FROM tbl_post');

$rows = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
// クエリキャッシュは使われません。
$rows = Yii::app()->db->createCommand($sql)->queryAll();
~~~

`cache()` メソッドに追加の `$queryCount` パラメータを供給することにより、複数のクエリに対してクエリキャッシングを行わせることが可能です。
次の例では、`cache()` をコールするときに、次の二つのクエリについて、クエリキャッシングを実行すべきことを指定しています。

~~~
[php]
// ...
$rows = Yii::app()->db->cache(1000, $dependency, 2)->createCommand($sql)->queryAll();
// クエリキャッシュが使われます。
$rows = Yii::app()->db->createCommand($sql)->queryAll();
~~~

周知のとおり、リレーショナルアクティブレコードのクエリを実行するときは、複数の SQL クエリが実行されます ([ロギング](/doc/guide/topics.logging)で確認できます)。
例えば、`Post` と `Comment` の関係が `HAS_MANY` である場合、以下のコードは、実際には、二つの DB クエリを実行します。

- 最初に post のレコードを 20件 を上限として選択します。
- 次に選択された post に対する comments を選択します。

~~~
[php]
$posts = Post::model()->with('comments')->findAll(array(
	'limit'=>20,
));
~~~

以下のようにクエリキャッシングを行うと、最初の DB クエリだけがキャッシュされます。

~~~
[php]
$posts = Post::model()->cache(1000, $dependency)->with('comments')->findAll(array(
	'limit'=>20,
));
~~~

両方の DB クエリをキャッシュするためには、追加のパラメータを与えて、次に何個の DB クエリをキャッシュしたいかを示さなければなりません。

~~~
[php]
$posts = Post::model()->cache(1000, $dependency, 2)->with('comments')->findAll(array(
	'limit'=>20,
));
~~~


### 制約

リソースハンドルを返すようなクエリにはクエリキャッシュは働きません。
例えばある種の DBMS において `BLOB` カラムタイプを用いる場合、クエリの結果はカラムデータについてリソースハンドルを返します。

ある種のキャッシュストレージはサイズに制約があります。
例えば、memcache では、各エントリのサイズは 1MB が上限値です。
そのため、クエリの結果のサイズがこの制約を越える場合、キャッシュは失敗します。
